use std::{fmt, marker};
use std::path::Path;
use std::fs::File;
use std::io::Write;
use std::io::{BufRead, BufReader};
use super::sphere::Sphere;
use super::traits::Skynet;
use super::distance;

struct Trans<T> {
    status: bool,
    body: fn(&Vec<T>) -> Vec<T>
}

impl<T> Trans<T> {
    fn new() -> Trans<T> {
        Trans{status: false, body: |_| unimplemented!()}
    }
}

fn vector_fmt<T: fmt::Display + marker::Copy>(v: &Vec<T>, s: &mut String) {
    let n = v.len();
    *s += "<";
    for i in 0 .. n - 1 {
        *s += format!("{:.6}, ", v[i]).as_str();
    }
    *s += format!("{:.6}>", v[n - 1]).as_str();
}
fn matrix_fmt<T: fmt::Display + marker::Copy>(matrix: &Vec<Vec<T>>,
                              prefix: &str,
                              trans: Trans<T>,
                              s: &mut String) {
    let n = matrix.len();
    // 前 n - 1 行
    for i in 0 .. n - 1 {
        *s += prefix;
        if trans.status {
            vector_fmt(&(trans.body)(&matrix[i]), s);
        } else {
            vector_fmt(&matrix[i], s);
        }
        *s += ",\n"
    }
    // 第 m 行
    *s += prefix;
    if trans.status {
        vector_fmt(&(trans.body)(&matrix[n - 1]), s);
    } else {
        vector_fmt(&matrix[n - 1], s);
    }
    *s += "\n";
}

pub struct Mesh<'a, T> {
    pub n: usize, // 维度
    pub name: &'a str, // 名字
    pub points: Vec<Vec<T>>,  // 点表
    pub facets: Vec<Vec<usize>> // 面表
}

impl<'a, T: Skynet<T>> Mesh<'a, T> {
    pub fn new(n: usize, name: &'a str) -> Mesh<'a, T> {
        return Mesh {n: n, name: name, points: Vec::new(), facets: Vec::new()};
    }
    pub fn load(&mut self, path: &Path) {
        let file = File::open(path).unwrap();
        let buf = BufReader::new(file);
        
        let mut lines_iter = buf.lines().map(|l| l.unwrap());
        assert_eq!(lines_iter.next(), Some(String::from("OFF")));
        let second_line = lines_iter.next().unwrap();
        let mut split = second_line.split_whitespace();
        let n_of_points: usize = split.next().unwrap().parse().unwrap();
        let n_of_facets: usize = split.next().unwrap().parse().unwrap();
        
        for _i in 0 .. n_of_points {
            let line = lines_iter.next().unwrap();
            let mut p: Vec<T> = Vec::new();
            for x in line.split_whitespace() {
                p.push(x.parse().unwrap());
            }
            self.points.push(p);
        }
        for _i in 0 .. n_of_facets {
            let line = lines_iter.next().unwrap();
            let mut f: Vec<usize> = Vec::new();
            let mut split = line.split_whitespace();
            let n:usize = split.next().unwrap().parse().unwrap();
            assert_eq!(n, self.n);
            for x in split {
                f.push(x.parse().unwrap());
            }
            assert_eq!(n, f.len());
            self.facets.push(f);
        }
    }
    pub fn bounding_sphere(&self) -> Sphere<T> {
        let mut sphere: Sphere<T> = Sphere::new(self.n);
        // 计算包围球中心
        let n: T = (self.points.len() as f64).into();
        for x_i in &self.points {
            for j in 0 .. self.n {
                sphere.center[j] += x_i[j] / n;
            }
        }
        // 计算包围球半径
        for x in &self.points {
            let d = distance(x, &sphere.center);
            if sphere.radius < d {
                sphere.radius = d;
            }
        }
        return sphere;
    }
    pub fn output_model(&self) {
        assert_eq!(self.n, 3);
        // 输出网格
        let m = self.points.len();
        let n = self.facets.len();
        let mut mesh = String::new();
        mesh += format!("#declare {}_data = mesh2 {{\n", self.name).as_str();
        mesh += "  vertex_vectors {\n";
        mesh += format!("    {},\n", m).as_str();
        matrix_fmt(&self.points,
                   "    ",
                   Trans{status: true,
                         body: |x| {
                             let n = x.len();
                             let mut new_x = vec![T::from(0.0); n];
                             for i in 0 .. n - 1 {
                                 new_x[i] = x[i];
                             }
                             new_x[n - 1] = x[n - 1].inv();
                             return new_x;
                         }
                   },
                   &mut mesh);
        mesh += "  }\n";
        mesh += format!("  face_indices {{\n").as_str();
        mesh += format!("    {},\n", n).as_str();
        matrix_fmt(&self.facets, "    ", Trans::new(), &mut mesh);
        mesh += "  }\n";
        mesh += "}\n";
        let mut mesh_file =
            File::create(Path::new(format!("{}_mesh.inc", self.name).as_str())).unwrap();
        mesh_file.write_all(mesh.as_bytes()).unwrap();

        // 输出顶点
        let mut vertices = String::new();
        vertices += format!("#declare {}_vertices_data = \
                              union {{\n", self.name).as_str();
        for p in &self.points {
            vertices += format!("  sphere {{<{:.6}, {:.6}, {:.6}> point_size}}\n",
                                p[0], p[1], p[2].inv()/* 右手系 -> 左手系 */).as_str();
        }
        vertices += "}\n";
        let mut vertices_file =
            File::create(Path::new(format!("{}_vertices.inc", self.name).as_str())).unwrap();
        vertices_file.write_all(vertices.as_bytes()).unwrap();

        // 输出边
        let mut edges = String::new();
        edges += format!("#declare {}_edges_data = union {{\n", self.name).as_str();
        for f in &self.facets {
            for i in 0 .. 3 {
                for j in i + 1 .. 3 {
                    edges += format!("  cylinder {{<{:.6}, {:.6}, {:.6}>, \
                                      <{:.6}, {:.6}, {:.6}>, line_width}}\n",
                                     &self.points[f[i]][0],
                                     &self.points[f[i]][1],
                                     &self.points[f[i]][2].inv(),/* 右手系 -> 左手系 */
                                     &self.points[f[j]][0],
                                     &self.points[f[j]][1],
                                     &self.points[f[j]][2].inv()/* 右手系 -> 左手系 */).as_str();
                }
            }
        }
        edges += "}\n";
         let mut edges_file =
            File::create(Path::new(format!("{}_edges.inc", self.name).as_str())).unwrap();
        edges_file.write_all(edges.as_bytes()).unwrap();

        // 输出 Povray 场景对象
        let mut object = String::new();
        object += "#include \"textures.inc\"\n";
        object += format!("#include \"{}_mesh.inc\"\n", self.name).as_str();
        object += "object {\n";
        object += format!("  {}_data\n", self.name).as_str();
        object += "  texture {pigment {color Gray50}}\n";
        object += "}\n\n";
        object += format!("#include \"{}_vertices.inc\"\n", self.name).as_str();
        object += "object {\n";
        object += format!("  {}_vertices_data\n", self.name).as_str();
        object += "  texture {Rust scale 0.3 * sky_r}\n";
        object += "}\n\n";
        object += format!("//#include \"{}_edges.inc\"\n", self.name).as_str();
        object += "//object {\n";
        object += format!("//  {}_edges_data\n", self.name).as_str();
        object += "//  texture {pigment {color Gray}}\n";
        object += "//}\n";
        let object_path = format!("{}_object.inc", self.name);
        let mut object_file = File::create(Path::new(object_path.as_str())).unwrap();
        object_file.write_all(object.as_bytes()).unwrap();
    }
}
